
// -> increase the number for smoother edges
$fn=30;
// -> the length of the box
totalLen=45.0;
// -> the width of the box
totalWid=20.0;
// -> the total height of the box 
totalHei=100.0;
// -> the radius of the side part
sideRad=4.0;
// -> the height of the roundings on top
topRad=5.0;
// -> the radius of fillets and outer roundings
filletRad=2.0;
// -> wall thickness
wallThic=1.6;
// -> the height of the lid
lidHei=15.0;
// -> the height of the overlapping part in the lid
lidOve=5.0;
// -> small amount that makes lid bigger than body in order to get a better fit between them on not so exact printers.
clearence=0.1;
////////////////////////////////////////////////////////////////////
// used for a very very very thin wall that will be extruded. 
minThic=0.05;
// size that is used for the object that cuts the containers in a plane
cutSize=max(totalLen,totalHei,totalWid);

    echo("-> main():");
    echo("    totalLen:  ", totalLen);
    echo("    totalWid:  ", totalWid);
    echo("    totalHei:  ", totalHei);
    echo("    sideRad:   ", sideRad);
    echo("    topRad:    ", topRad);
    echo("    filletRad: ", filletRad);
    echo("    wallThic:  ", wallThic);
    echo("    lidHei:    ", lidHei);
    echo("    lidOve:    ", lidOve);
    echo("    clearence: ", clearence);
    echo("    minThic:   ", minThic);
    echo("    cutSize:   ", cutSize);

////////////////////////////////////////////////////////////////////
// main
////////////////////////////////////////////////////////////////////

translate([-9.5*totalWid,0,0])
    pluggableContainer()
        polygon([[-totalWid,totalWid],[totalWid,totalWid],[0,-totalWid]]);

translate([-7*totalWid,0,0])
    pluggableContainer()
        circle(r=totalWid, $fn=5);

translate([-4.5*totalWid,0,0])
    pluggableContainer()
        circle(r=totalWid, $fn=6);
        
translate([-2*totalWid,0,0])
    pluggableContainer()
        circle(r=totalWid, $fn=8);

translate([totalWid/2,0,0])
    pluggableContainer()
        circle(r=totalWid);

translate([3*totalWid,0,0])
     pluggableContainer()
        square([totalLen, totalWid], center=true);
        
translate([6*totalWid,0,0])
     pluggableContainer()
        makeBase00(totalLen, totalWid, sideRad);

translate([9*totalWid,0,0])
    pluggableContainer00();
    
translate([12*totalWid,0,0])
    pluggableContainer01();

translate([15*totalWid,0,0])
    pluggableContainer()
        scale([totalLen, totalWid]) 
            circle(d=1);

translate([18*totalWid,0,0])
    pluggableContainer02();

////////////////////////////////////////////////////////////////////
// modules
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
module pluggableContainer()
{
makeBody(totalHei, filletRad, wallThic, lidHei, lidOve)
    children(0);

translate([0,-2*totalWid,0]) 
    makeLid(totalHei, filletRad, wallThic, lidHei, lidOve)
        children(0);
}
////////////////////////////////////////////////////////////////////
module makeBody(height, fillet, wall, lid, overlap)
{
    union()
    {
        cutTop(height-lid)
            makeOuterBody(fillet, wall)
                children(0);

        // inner body
        cutTop(height-lid+overlap)
            makeInnerBody(fillet, wall)
                children(0);
    }
}
////////////////////////////////////////////////////////////////////
module makeLid(height, fillet, wall, lid, overlap)
{
    union()
    {
        cutTop(lid)
            makeOuterBody(fillet, wall)
                offset(clearence)
                    children(0);

        // inner body
        cutTop(lid-overlap)
            makeInnerBody(fillet, wall)
                offset(clearence)
                children(0);
    }
}
////////////////////////////////////////////////////////////////////
module makeOuterBody(fillet, wall)
{
    difference()
    {
        roundedBox(fillet)
            children(0);

        translate([0,0,wall/2])
            roundedBox(fillet)
                offset(-wall/2)
                    children(0);
    }
}
////////////////////////////////////////////////////////////////////
module makeInnerBody(fillet, wall)
{
    translate([0,0,wall/2])
    difference()
    {
        roundedBox(fillet)
            offset(-wall/2)
                children(0);

        translate([0,0,wall/2])
            roundedBox(fillet)
                offset(-wall)
                    children(0);
    }
}
//////////////////////////////////////////////////////////////////////
module roundedBox(filletRadius)
{
    hull()
    {
        translate([0,0,totalHei-filletRad])
            makeTop(filletRadius)
                offset(-filletRad)
                    children(0);

        translate([0,0,filletRad])
            makeBottom(filletRadius)
                offset(-filletRad)
                    children(0);
    }
}
////////////////////////////////////////////////////////////////////
module makeTop(filletRadius)
{    
    cutBottom(0)
        makeFillet(filletRadius)
            children(0);
}
////////////////////////////////////////////////////////////////////
module makeBottom(filletRadius)
{
    cutTop(0)
        makeFillet(filletRadius)
            children(0);
}
////////////////////////////////////////////////////////////////////
module makeFillet(filletRadius)
{
    minkowski()
    {
    sphere(r=filletRadius);
    translate([0,0,-minThic/2])
    linear_extrude(minThic, convexity=8)
        children(0);
    }
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
module pluggableContainer00()
{
makeBody00();

rotate([180,0,0])
    translate([0,2*totalWid,-totalHei]) 
        makeLid00();
}
////////////////////////////////////////////////////////////////////
module pluggableContainer01()
{
makeBody01();

rotate([180,0,0])
    translate([0,2*totalWid,-totalHei]) 
        makeLid01();
}
////////////////////////////////////////////////////////////////////
module pluggableContainer02()
{
makeBody02();

rotate([180,0,0])
    translate([0,2*totalWid,-totalHei]) 
        makeLid02();
}
////////////////////////////////////////////////////////////////////
module makeBody00()
{
    union()
    {
        // exterior part
        cutTop(totalHei-lidHei)
        difference()
        {
            roundedBox00(totalHei, filletRad)
                makeBase00(totalLen, totalWid, sideRad);

            translate([0,0,wallThic/2])
                    roundedBox00(totalHei, filletRad)
                        offset(-wallThic/2)
                            makeBase00(totalLen, totalWid, sideRad);
        }
        
        // interior part
        cutTop(totalHei-lidHei+lidOve)
        translate([0,0,wallThic/2])
        difference()
        {
            roundedBox00(totalHei, filletRad)
                offset(-wallThic/2)
                    makeBase00(totalLen, totalWid, sideRad);

            translate([0,0,wallThic/2])
                roundedBox00(totalHei, filletRad)
                    offset(-wallThic)
                        makeBase00(totalLen, totalWid, sideRad);
        }
    }
}
 ////////////////////////////////////////////////////////////////////
module makeBody01()
{
    union()
    {
        // exterior part
        cutTop(totalHei-lidHei)
        translate([0,0,filletRad])
        difference()
        {
            roundObject(filletRad)
                roundedBox00(totalHei, filletRad)
                    offset(-filletRad)
                        makeBase00(totalLen, totalWid, sideRad);

            translate([0,0,wallThic/2])
                roundObject(filletRad)
                    roundedBox00(totalHei, filletRad)
                        offset(-wallThic/2-filletRad)
                            makeBase00(totalLen, totalWid, sideRad);
        }
        
        // interior part
        cutTop(totalHei-lidHei+lidOve)
        translate([0,0,wallThic/2+filletRad])
        difference()
        {
            roundObject(filletRad)
                roundedBox00(totalHei, filletRad)
                    offset(-wallThic/2-filletRad)
                        makeBase00(totalLen, totalWid, sideRad);

            translate([0,0,wallThic/2])
                roundObject(filletRad)
                    roundedBox00(totalHei, filletRad)
                        offset(-wallThic-filletRad)
                            makeBase00(totalLen, totalWid, sideRad);
        }
    }
 }
////////////////////////////////////////////////////////////////////
module makeBody02()
{
    halfWall=wallThic/2; // the total hull is a sum of exterior and interiour part
    union()
    {
        // exterior part
        cutTop(totalHei-lidHei)
        difference()
        {
            roundedBox01(totalLen, totalWid, totalHei, topRad, filletRad);    

            translate([0,0,halfWall])
                roundedBox01(totalLen-wallThic, totalWid-wallThic, 
                    totalHei-wallThic, topRad-wallThic/2, filletRad);        
        }
        
        // interior part
        cutTop(totalHei-lidHei+lidOve)
        difference()
        {
            roundedBox01(totalLen-wallThic+0.001, totalWid-wallThic+0.001, 
                         totalHei-wallThic+0.001, topRad-wallThic/2+0.0005, filletRad);
            
            translate([0,0,halfWall])
                roundedBox01(totalLen-2*wallThic, totalWid-2*wallThic, 
                             totalHei-2*wallThic, topRad-wallThic, filletRad);
        }
    }
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
module makeLid00()
{
    union()
    {
        // exterior part
        cutBottom(totalHei-lidHei)
        difference()
        {
            roundedBox00(totalHei, filletRad)
                offset(clearence)
                    makeBase00(totalLen, totalWid, sideRad);

            translate([0,0,-wallThic/2])
                    roundedBox00(totalHei, filletRad)
                        offset(-wallThic/2+clearence)
                            makeBase00(totalLen, totalWid, sideRad);
        }
        
        // interior part
        cutBottom(totalHei-lidHei+lidOve)
        translate([0,0,-wallThic/2])
        difference()
        {
            roundedBox00(totalHei, filletRad)
                offset(-wallThic/2+clearence)
                    makeBase00(totalLen, totalWid, sideRad);
         
            translate([0,0,-wallThic/2])
                roundedBox00(totalHei, filletRad)
                    offset(-wallThic+clearence)
                        makeBase00(totalLen, totalWid, sideRad);
        }
    }
}
////////////////////////////////////////////////////////////////////
module makeLid01()
{
    union()
    {
        // exterior part
        cutBottom(totalHei-lidHei)
        translate([0,0,-filletRad])
        difference()
        {
            roundObject(filletRad)
                roundedBox00(totalHei, filletRad)
                    offset(-filletRad+clearence)
                        makeBase00(totalLen, totalWid, sideRad);

            translate([0,0,-wallThic/2])
                roundObject(filletRad)
                    roundedBox00(totalHei, filletRad)
                        offset(-wallThic/2-filletRad+clearence)
                            makeBase00(totalLen, totalWid, sideRad);
        }
        
        // interior part
        cutBottom(totalHei-lidHei+lidOve)
        translate([0,0,-wallThic/2-filletRad])
        difference()
        {
            roundObject(filletRad)
                roundedBox00(totalHei, filletRad)
                    offset(-wallThic/2-filletRad+clearence)
                        makeBase00(totalLen, totalWid, sideRad);
         
            translate([0,0,-wallThic/2])
                roundObject(filletRad)
                    roundedBox00(totalHei, filletRad)
                        offset(-wallThic-filletRad+clearence)
                            makeBase00(totalLen, totalWid, sideRad);
        }
    }
}
////////////////////////////////////////////////////////////////////
module makeLid02()
{
    halfWall=wallThic/2.0; // the total hull is a sum of exterior and interiour part
    union()
    {
        // exterior part
        cutBottom(totalHei-lidHei)
        difference()
        {
            roundedBox01(totalLen+2*clearence, totalWid+2*clearence, totalHei, topRad, filletRad);    
            
            translate([0,0,halfWall-0.01])
                roundedBox01(totalLen+2*clearence-wallThic, totalWid+2*clearence-wallThic, 
                    totalHei-wallThic+0.01, topRad-wallThic/2, filletRad);    
        }

        // interior part
        cutBottom(totalHei-lidHei+lidOve-halfWall)
        difference()
        {
            roundedBox01(totalLen+2*clearence-wallThic+0.001, totalWid+2*clearence-wallThic+0.001, 
                    totalHei-wallThic+0.001, topRad-wallThic/2+0.0005, filletRad);
            
            translate([0,0,halfWall-0.01])
                roundedBox01(totalLen+2*clearence-2*wallThic, totalWid+2*clearence-2*wallThic, 
                             totalHei-2*wallThic+0.01, topRad-wallThic, filletRad);
        }
    }
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
module roundedBox00(height, filletRadius)
{
    assert(topRad<=totalLen, "Error: top cuvature may not be greater than length of box.");
    intersection()
    {
        hull()
        {
            translate([0,0,height-filletRadius])
                makeFillet(filletRadius)
                    offset(-filletRadius)
                        children(0);

            translate([0,0,filletRadius])
                makeFillet(filletRadius)
                    offset(-filletRadius)
                        children(0);
        }

        // @see https://de.wikipedia.org/wiki/Kreissegment
        //sphereRad=(4h² + s²)/8h = (4*topRad² + length²)/8*topRad
        sphereRad= (4*(topRad*topRad) + totalLen*totalLen)/(8*topRad);
        translate([0,0,height-sphereRad])
            sphere(r=sphereRad, $fn=200); 
    }
}
////////////////////////////////////////////////////////////////////
module roundedBox01(length, width, height, topRad, filletRad)
{
    filletDia=2*filletRad;
    topDia=2*topRad;
    hull()
    {
      translate([0,0,height-topRad])
            scale([length, width, topDia]) 
                sphere(d=1);

        translate([0, 0, filletRad+0.00005])
        minkowski()
        {
            sphere(r=filletRad);
            scale([length-filletDia, width-filletDia, 1]) cylinder(d=1, h=0.0001);
        }
    }
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
module makeBase00(length, width, sideRad)
{
    sideDia=2*sideRad;
    translate([-length/2+sideRad,0])
    hull()
    {
        translate([length-sideDia,0]) 
            scale([sideDia, width]) 
                circle(d=1);
        scale([sideDia, width]) 
            circle(d=1);
    }
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// cuts the top of a children away.
module cutTop(height2Cut)
{
    difference()
    {
        children(0);
        translate([0, 0, height2Cut+cutSize])
            cube(2*cutSize, center=true);
    }
}
////////////////////////////////////////////////////////////////////
// cuts the bottom of a children away.
module cutBottom(height2Cut)
{
    difference()
    {
        children(0);
        translate([0, 0, height2Cut-cutSize])
            cube(2*cutSize, center=true);
    }
}
////////////////////////////////////////////////////////////////////
// rounds a 3d object with an given radius
module roundObject(radius)
{
    minkowski()
    {
        sphere(radius);
        children(0);
    }
}
